Skip to main content

Building the Toolchain

Cross compilation is a complicated topic at the best of times. Luckily, with the LLVM project it is somewhat easier than it was a decade ago.

QEMU

The alternative to this setup is to spin up a QEMU user-space emulation session and then compile the program in there. You will likely need to recompile clang within the emulator, but you will not have to fiddle with linking parameters to the sysroot since the emulator will already have the proper sysroot installed and it will be as if you were installing clang on your native system. All in all, I recommend the QEMU strat, after having played with this cross compilation toolchain for an obscene amount of time.

Required Sources

Attribution

This tutorial is based on:

Thanks to those project without which this tutorial really would have been impossible.

In order to construct your cross-compilation toolchain, you will require the following repositories:

TODO

complete list of system deps, or better yet a nix flake

Compiling the RISCV-GNU toolchain

The toolchain provided by GNU gives us:

  • The RISCV sysroot.
  • The gcc compiler for RISCV (clang has a tough time linking with the RISCV sysroot)
  • A number of other utilities

For reference (and so that you really understand what that means), the sysroot of a system is the necessary system libraries and tools against which standard utilities link to call OS functions.

sysroots

If you want to read more about the sysroots and all the fun stuff that you can read at the following sites:

  1. Clone the repo: git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git

  2. Change to the directory

  3. Configure: When compiling the toolchain, you will need to configure it rather liberally. The configuration command I used was:

    ./configure --prefix=$HOME/riscv --enable-multilib --disable-gdb

    For a little more detail:

    • --prefix specifies the install directory where you want your utilities to end up
    • --enable-multilib enables multilib TODO I'm not sure what this means exactly
    • --disable-gdb since depending on the version you build, gdb will require gmp which is not necessarily installed on the lab machines.
  4. After you have configured to your specifications, you can simply run make and wait for a bit. Once again, I suggest doing so inside a tmux session.

Simple script

mkdir $HOME/riscv
https://github.com/riscv-collab/riscv-gnu-toolchain.git riscv-toolchain
cd riscv-toolchain
./configure --prefix=$HOME/riscv --enable-multilib --disable-gdb
make
warning

You will likely run out of space on your lab machine if you try to build gem5, the toolchain and clang on the same machine. Consider teaming up if you really want to do this, or maybe using your own machines (after you switch to linux 🧐).

Compiling clang

This is just a good thing to know, so sit tight and hang on.

binutils

You may get some errors in the build process. If this is the case for you, don't worry, just continue reading through the binutils section below.

  1. Clone the repo: git clone https://github.com/llvm/llvm-project
  2. Make a build directory: mkdir build && cd build
  3. Configure:
    cmake -DLLVM_TARGETS_TO_BUILD="RISCV" \
    -DLLVM_PARALLEL_LINK_JOBS=2 \
    -DLLVM_USE_SPLIT_DWARF=True \
    -DLLVM_BUILD_TESTS=False \
    -DDEFAULT_SYSROOT=$HOME/riscv \
    -DLLVM_DEFAULT_TARGET_TRIPLE=riscv64-unknown-elf \
    -DLLVM_INCLUDE_BENCHMARKS=OFF \
    -DLLVM_INCLUDE_EXAMPLES=OFF \
    -DLLVM_INSTALL_UTILS=ON \
    -DLLVM_ENABLE_ASSERTIONS=ON \
    -DLLVM_BINUTILS_INCDIR="/usr/include" \ # you might have issues with this
    -DBUILD_SHARED_LIBS=ON \
    -DLLVM_USE_LINKER=ld \ # if you have lld, you should probably use it
    -DCMAKE_C_COMPILER=gcc \ # if you have clang, you should probably use it
    -DCMAKE_CXX_COMPILER=g++ \ # ^^
    -DCMAKE_BUILD_TYPE=Release \
    -G Ninja ../llvm
    if you want a more in-depth explanation of these flags, go to the llvm discourse or the llvm documentation, and remember you can probably ask the TA about these things too.
  4. Run the regular make job, but this time with ninja ninja -j4
  5. Make a directory in which you want the installed files to be: mkdir llvm-18
  6. Cmake install them: cmake --install . --prefix $BASE_DIR/llvm-18, where $BASE_DIR is some directory of your choice

Simplified Script

#!/usr/bin/env sh

# Create and enter build directory
if [[ "$1" = "-i" ]]
then
BUILD_DIR="\${HOME}/llvm"
mkdir "\${BUILD_DIR}"
cd "\${BUILD_DIR}"
git clone --depth 1 --branch llvmorg-17.0.0 https://github.com/llvm/llvm-project.git llvm-project
else
BUILD_DIR="${HOME}/llvm"
fi

cd llvm-project
mkdir -p build
cd build
# Build llvm, clang, mlir, and lld
# -DLLVM_ENAVBLE_PROJECTS="clang;mlir;lld" \
# lldb;clang-tools-extra
cmake -DLLVM_TARGETS_TO_BUILD="RISCV" \
-DLLVM_PARALLEL_LINK_JOBS=2 \
-DLLVM_USE_SPLIT_DWARF=True \
-DLLVM_BUILD_TESTS=False \
-DDEFAULT_SYSROOT=/home/achilibe/Code/riscv-gnu-toolchain/installed-tools/riscv64-unknown-elf \
-DLLVM_DEFAULT_TARGET_TRIPLE=riscv64-unknown-elf \
-DLLVM_INCLUDE_BENCHMARKS=OFF \
-DLLVM_INCLUDE_EXAMPLES=OFF \
-DLLVM_INSTALL_UTILS=ON \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DLLVM_BINUTILS_INCDIR="/home/achilibe/Code/binutils/include" \
-DBUILD_SHARED_LIBS=ON \
-DLLVM_USE_LINKER=lld-15 \
-DCMAKE_C_COMPILER=clang-15 \
-DCMAKE_CXX_COMPILER=clang++-15 \
-DCMAKE_BUILD_TYPE=Release \
-G Ninja ../llvm
ninja -j4

DIRNAME="llvm-18-rv64"
mkdir "${BUILD_DIR}/$DIRNAME"
cmake --install . --prefix "${BUILD_DIR}/$DIRNAME"

rm -r "\${BUILD_DIR}/llvm-project/build"

Getting some sort of Missing File Error?

If you obtain a missing file error similar to the following:

CMake Error at tools/lto/cmake_install.cmake:52 (file):
file INSTALL cannot find
".../llvm/llvm-project/build/lib/libLTO.so.18.1": No such file or directory.
Call Stack (most recent call first):
tools/cmake_install.cmake:47 (include)
cmake_install.cmake:127 (include)

Then you need to build binutils. This is a pretty common problem, unfortunately it takes some extra time...

Building Binutils

  1. Clone the binutils repo: git clone --depth 1 --branch binutils-2_39 https://sourceware.org/git/binutils-gdb.git binutils
  2. cd binutils
  3. Create a build directory: mkdir build && cd build
  4. Configure: ../configure --enable-gold --enable-plugins --disable-werror
  5. make

Script

Once you make the binutils, you can then set the build directory as the -DLLVM_BINUTILS_INCDIR for the llvm configuration and all should work.

git clone --depth 1 --branch binutils-2_39 \
https://sourceware.org/git/binutils-gdb.git binutils
cd binutils
mkdir build; cd build
../configure --enable-gold --enable-plugins --disable-werror
make all-gold